package cn.kgc.vue.shiro;

import cn.kgc.vue.entity.RolePermission;
import cn.kgc.vue.entity.UserRole;
import cn.kgc.vue.mapper.PermissionMapper;
import cn.kgc.vue.mapper.RolePermissionMapper;
import cn.kgc.vue.mapper.UserRoleMapper;
import cn.kgc.vue.utils.IpUtil;
import cn.kgc.vue.utils.JWTUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.data.redis.core.StringRedisTemplate;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author 课工场
 * @date 2024/9/20
 * @description
 */
@Slf4j
public class CustomerRealm extends AuthorizingRealm {

    @Resource
    private JWTUtil jwtUtil;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private UserRoleMapper userRoleMapper;

    @Resource
    private RolePermissionMapper rolePermissionMapper;

    @Resource
    private PermissionMapper permissionMapper;

    @Override
    public boolean  supports(AuthenticationToken token){
        return  token instanceof  JWTToken;
    }
    // 授权方法
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        // uid
        Object primaryPrincipal = principals.getPrimaryPrincipal();

        // 根据用户id 查询用户角色id   user_role
        LambdaQueryWrapper<UserRole> lambda = new QueryWrapper<UserRole>().lambda();
        lambda.eq(UserRole::getUserId,primaryPrincipal);

        List<Integer> rids = userRoleMapper.selectList(lambda).stream()
                .map(ur -> ur.getRoleId())
                .collect(Collectors.toList());

        // 根据角色id  权限id     role_permission
        LambdaQueryWrapper<RolePermission> lambda1 = new QueryWrapper<RolePermission>().lambda();
        lambda1.in(RolePermission::getRoleId,rids);

        Set<Integer> pids = rolePermissionMapper.selectList(lambda1).stream()
                .map(rp -> rp.getPerId())
                .collect(Collectors.toSet());

        // 根据权限id  查询权限 字符串
        List<String> permissionStrs = permissionMapper.selectBatchIds(pids).stream()
                .map(p -> p.getPermission())
                .collect(Collectors.toList());


        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        simpleAuthorizationInfo.addStringPermissions(permissionStrs);

        return simpleAuthorizationInfo;
    }


    // 认证方法
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

        JWTToken jwtToken = (JWTToken) token;

        String tken = (String) jwtToken.getPrincipal();
        // 校验token的真伪
        jwtUtil.verifyToken(tken);

        // token 劫持    token  :  ip
        String redisIp = stringRedisTemplate.opsForValue().get(tken);
        HttpServletRequest servletRequest = (HttpServletRequest) jwtToken.getServletRequest();
        String requestIp = IpUtil.getIp(servletRequest);

        if (!StringUtils.equals(requestIp,redisIp)){
            throw  new RuntimeException("token异常,请重新登录...");
        }

        // 解析token  获取用户信息
        Map<String, Object> claims = jwtUtil.getClaims(tken);
        Object uid = claims.get("uid");

        // 创建AuthenticationInfo 类型对象  返回
        SimpleAuthenticationInfo simpleAuthenticationInfo =
                new SimpleAuthenticationInfo(uid, tken, this.getName());

        return simpleAuthenticationInfo;
    }
}
